package org.zstacks.znet;

import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.ServerSocketChannel;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.zstacks.znet.log.Logger;
import org.zstacks.znet.nio.Dispatcher;
import org.zstacks.znet.nio.Session;

public class RemotingServer extends MessageAdaptor implements Closeable{  
	private static final Logger log = Logger.getLogger(RemotingServer.class); 
	
	protected Map<String, MessageHandler> handlerMap = new ConcurrentHashMap<String, MessageHandler>();
	protected MessageHandler globalHandler; 
	
	protected Dispatcher dispatcher;
	protected String host = "0.0.0.0";
	protected int port;
	protected final String serverAddr;
	protected String serverName = "RemotingServer";
	protected ServerSocketChannel serverChannel;
	
	public RemotingServer(int port, Dispatcher dispatcher) { 
		this("0.0.0.0", port, dispatcher);
	}
	
	public RemotingServer(String host, int port, Dispatcher dispatcher) { 
		this.dispatcher = dispatcher;
		this.host = host;
		this.port = port; 
		//
		this.dispatcher.serverIoAdaptor(this);
		
	  	if("0.0.0.0".equals(this.host)){
    		this.serverAddr = String.format("%s:%d", Helper.getLocalIp(), this.port);
    	} else {
    		this.serverAddr = String.format("%s:%d", this.host, this.port);
    	}
		this.registerHandler(Message.HEARTBEAT, new MessageHandler() { 
			public void handleMessage(Message msg, Session sess) throws IOException { 
				//ignore
			}
		});
	}
	
   
	public void registerHandler(String command, MessageHandler handler){
    	this.handlerMap.put(command, handler);
    }
    
    public void registerGlobalHandler(MessageHandler beforeHandler) {
		this.globalHandler = beforeHandler;
	}  
    
    public String findHandlerKey(Message msg){
    	return msg.getCommand();
    }
    
    public void onMessage(Object obj, Session sess) throws IOException {  
    	Message msg = (Message)obj;  
    	if(this.globalHandler != null){
    		this.globalHandler.handleMessage(msg, sess);
    	}
    	
    	String cmd = findHandlerKey(msg);
    	if(cmd == null){ 
    		Message res = new Message();
    		res.setMsgId(msg.getMsgId()); 
        	res.setStatus("400");
        	res.setBody("Bad format: missing command"); 
        	sess.write(res);
    		return;
    	}
    	
    	MessageHandler handler = handlerMap.get(cmd);
    	if(handler != null){
    		handler.handleMessage(msg, sess);
    		return;
    	}
    	
    	Message res = new Message();
    	res.setMsgId(msg.getMsgId()); 
    	res.setStatus("400");
    	String text = String.format("Bad format: command(%s) not support", cmd);
    	res.setBody(text); 
    	sess.write(res); 
    }  
    
    public void start() throws IOException{  
    	if(serverChannel != null){
    		log.info("server already started");
    		return;
    	}
    	if(!this.dispatcher.isStarted()){
			this.dispatcher.start();
		}
    	serverChannel = dispatcher.registerServerChannel(host, port);
    	log.info("%s serving@%s:%d", this.serverName, this.host, this.port);
    }
    
    @Override
    public void close() throws IOException { 
    	if(serverChannel != null){
    		serverChannel.close();
    	}
    }
}

